<?php

namespace Core;

use Core\Contracts\Middleware;

/**
 * Class Router
 *
 * 负责解析请求 URI，通过中间件管道，
 * 并将其分发到适当的控制器和方法。
 *
 * @package Core
 */
class Router
{
    /**
     * @var Request 当前的请求对象
     */
    private Request $request;

    /**
     * @var array 注册的中间件类 (FQN - 完全限定名称)
     */
    private array $middleware = [];

    /**
     * @var string 控制器所在的命名空间
     */
    private string $controllerNamespace = 'App\\Controllers\\';

    /**
     * Router 构造函数.
     *
     * @param Request $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * 注册一个中间件。
     * 中间件将按照它们被添加的顺序执行。
     *
     * @param string $middlewareClass 实现了 Middleware 接口的类名
     * @return void
     * @throws \InvalidArgumentException 如果类不存在或未实现 Middleware 接口
     */
    public function addMiddleware(string $middlewareClass): void
    {
        if (!class_exists($middlewareClass)) {
            throw new \InvalidArgumentException("Middleware class not found: {$middlewareClass}");
        }
        
        if (!is_subclass_of($middlewareClass, Middleware::class)) {
            throw new \InvalidArgumentException("Middleware {$middlewareClass} must implement " . Middleware::class);
        }
        
        $this->middleware[] = $middlewareClass;
    }

    /**
     * 分发请求到控制器。
     * 此方法解析 URI，构建中间件管道，并执行它。
     *
     * @return Response
     */
    public function dispatch(): Response
    {
        try {
            // 1. 解析控制器和方法
            [$controllerClass, $method] = $this->resolveRoute();

            // 2. 检查控制器和方法是否存在
            if (!class_exists($controllerClass)) {
                return $this->errorResponse(404, "Controller not found: {$controllerClass}");
            }
            
            // 假设控制器没有构造函数依赖
            $controller = new $controllerClass();

            if (!method_exists($controller, $method)) {
                return $this->errorResponse(404, "Method not found: {$controllerClass}::{$method}");
            }
            
            // 3. 定义最终要执行的控制器动作
            $controllerAction = function (Request $request) use ($controller, $method): Response {
                // 控制器方法必须返回一个 Response 对象
                $response = $controller->{$method}($request);
                
                // 健壮性检查：确保返回的是 Response 对象
                if (!$response instanceof Response) {
                    throw new \LogicException(
                        "Controller {$controllerClass}::{$method} must return an instance of " . Response::class
                    );
                }
                return $response;
            };

            // 4. 构建中间件管道
            $pipeline = $this->createPipeline($controllerAction);

            // 5. 执行管道
            return $pipeline($this->request);

        } catch (\Throwable $e) {
            // 捕获所有运行时错误（包括中间件或控制器中的错误）
            Logger::error($e); // 使用我们之前优化的 Logger
            return $this->errorResponse(500, "Application Error: " . $e->getMessage());
        }
    }

    /**
     * 解析 URI 以确定控制器和方法。
     * /           -> Home::index
     * /user       -> User::index
     * /user/profile -> User::profile
     *
     * @return array [string $controllerClass, string $methodName]
     */
    private function resolveRoute(): array
    {
        $uri = $this->request->getUri();
        $parts = array_filter(explode('/', trim($uri, '/')));

        // 默认控制器和方法
        $controllerName = 'Home';
        $methodName = 'index';

        if (!empty($parts)) {
            // 第一个部分是控制器 (e.g., 'user' -> 'User')
            $controllerName = ucfirst(array_shift($parts));
        }

        if (!empty($parts)) {
            // 第二个部分是方法 (e.g., 'profile' -> 'profile')
            $methodName = array_shift($parts);
        }
        
        // 忽略 URI 的其余部分（/a/b/c），控制器可以自己从 Request 中获取

        return [
            $this->controllerNamespace . $controllerName,
            $methodName
        ];
    }

    /**
     * 创建中间件执行管道。
     * 这是一个洋葱模型：请求从外到内，响应从内到外。
     *
     * @param callable $finalAction 最终要执行的控制器动作
     * @return callable
     */
    private function createPipeline(callable $finalAction): callable
    {
        // 管道的最后一层是控制器动作
        $pipeline = $finalAction;
        
        // 反向遍历中间件，将它们一层层包裹在控制器外面
        $middlewareStack = array_reverse($this->middleware);

        foreach ($middlewareStack as $middlewareClass) {
            $pipeline = function (Request $request) use ($middlewareClass, $pipeline): Response {
                // 实例化中间件（假设没有构造函数依赖）
                /** @var Middleware $middleware */
                $middleware = new $middlewareClass();
                
                // 执行中间件的 handle 方法，将下一层（$pipeline）作为 $next 传递
                return $middleware->handle($request, $pipeline);
            };
        }
        
        // 返回完整的、可执行的管道（一个闭包）
        return $pipeline;
    }

    /**
     * 辅助方法：创建一个错误的响应对象。
     *
     * @param int $code 状态码
     * @param string $message 错误信息
     * @return Response
     */
    private function errorResponse(int $code, string $message): Response
    {
        $response = new Response();
        // 在生产环境中，你可能不想暴露详细的 $message
        return $response->status($code)->text($message);
    }
}